<template>
  <div class="box">
    <!--专门存储弹窗-->
    <div>
      <!--群聊信息弹窗-->
      <Modal
          v-model="addGroupModal"
          title="新建群聊"
          @on-ok="addGroup"
          @on-cancel="editVisible = false">
        <table>
          <tr>
            <td>群聊名称</td>
            <td>
              <input type="text" v-model="groupName"/>
            </td>
          </tr>
          <tr>
            <td>群聊头像</td>
            <td>
              <img :src="filePath" width="40" height="40" style="border-radius: 40px;display: inline-block">
              <Upload :before-upload="upload" style="display: inline-block;vertical-align: top;margin: 10px 0 0 10px">
                <Button icon="ios-cloud-upload-outline">上传头像</Button>
              </Upload>
            </td>
          </tr>
        </table>
      </Modal>

      <!--删除成员弹窗-->
      <Modal
          v-model="removeModal"
          title="选择成员"
          @on-cancel="removeModal = false">
        <List>
          <!--踢出用户列表, 排除自己-->
          <ListItem v-for="item in currentGroup?.['userList']" v-show="item.id !== currentId">
            <SXQUserItem :user="item"/>
            <!--将要删除的用户id传入-->
            <Button type="error" ghost style="margin-left: 350px" @click="remove(item.id, item.nickname)">踢出
            </Button>
          </ListItem>
        </List>
      </Modal>
    </div>

    <!--左侧选项容器-->
    <div class="option">
      <!--当前用户头像-->
      <div class="avatar"><img :src="currentUser.avatar" class="img"></div>
      <!--选项列表-->
      <List border size="small">
        <div :class="item.clazz" v-for="(item, index) in optionStyle" @click="changeOption(index)">
          <ListItem>
            <Icon :type="item.type" size="25"/>
          </ListItem>
        </div>
      </List>
    </div>

    <!--群聊列表容器-->
    <div class="list">
      <!--搜索框-->
      <div class="search">
        <input type="text" placeholder="筛选群聊"/>
        <!--添加群聊按钮-->
        <Button icon="ios-add-circle-outline" @click="addGroupModal = true"></Button>
      </div>

      <!--群聊列表-->
      <div class="group-list">
        <List>
          <div :class="item.style" v-for="(item, index) in groups" @click="changeGroup({id: item.id, index: index})">
            <ListItem>
              <ListItemMeta class="chat-list" :avatar="item.avatar" :title="item.name"/>
            </ListItem>
          </div>
        </List>
      </div>
    </div>

    <!--聊天室页面-->
    <div class="chat-room">
      <!--顶部信息栏-->
      <div class="chat-top">
        <img :src="currentGroup?.avatar" class="img" v-show="currentGroup?.avatar">
        <span>{{ currentGroup?.name }}</span>
      </div>

      <!--消息展示容器-->
      <div class="chatBox">
        <!--用主键作为key, 防止出现diff错乱-->
        <ChatItem :key="item.key"
                  :is-self="currentId === item.userId"
                  :sender="item.messageSender"
                  :content="item.messageContent"
                  :avatar="item.avatar"
                  :time="item.createTime"
                  v-for="item in nowChat"/>
      </div>

      <!--输入区域-->
      <div class="input-box">
        <input type="text" v-model="sendData" @keydown.enter="sendGroupMessage"/>
        <Button icon="ios-send" @click="sendGroupMessage"></Button>
      </div>
    </div>

    <!--设置区域-->
    <div class="setting">
      <div class="search" style="background: white">
        <input type="text" placeholder="搜索群成员"/>
      </div>
      <div class="member">
        <!--展示群成员, 删除群成员, 增加群成员-->
        <table>
          <!--防止diff错乱-->
          <tr v-for="(_, i) in gridHeight(groupMember.length)" :key="i + Date.now()">
            <td v-for="(_, j) in 4" :key="j + Date.now()">
              <SXQUserItem :user="groupMember[i * 4 + j]"/>
            </td>
          </tr>
          <tr>
            <!--新增成员-->
            <td>
              <Button icon="ios-add-circle-outline" style="margin-left: 20px">添加</Button>
            </td>
            <!--踢出成员, 只有群主才能操作-->
            <td>
              <Button icon="md-remove" @click="removeModal = true" v-if="currentGroup?.userId === currentId">移出</Button>
            </td>
          </tr>
        </table>
      </div>
    </div>
  </div>
</template>

<script>
import ChatItem from "@/components/ChatItem";
import {
  createGroup,
  fileUpload,
  getGroupChat,
  getGroupInfo,
  getUser,
  getUserByPhone,
  getUserJoinGroup, removeMember,
} from "@/api/api";
import SXQUserItem from "@/components/SXQ-UserItem";

export default {
  components: {SXQUserItem, ChatItem},
  data() {
    return {
      removeModal: false,
      groupName: "",
      filePath: "",
      addGroupModal: false,
      currentGroup: null,
      username: null,
      currentId: null,
      currentUser: null,
      optionStyle: [
        {
          type: "ios-chatboxes",
          clazz: "option-list"
        },
        {
          type: "md-person",
          clazz: "option-list"
        }
      ],
      groupStyle: [],
      groups: [],
      groupsChat: null,
      systemChat: [],
      nowChat: [],
      sendData: null,
      groupReceiver: null,
      groupMember: [],
      stompClient: null,
    }
  },

  computed: {
    gridHeight() {
      return (size) => size % 4 === 0 ? parseInt(size / 4) : parseInt(size / 4) + 1
    },
  },
  beforeMount() {
    let user = JSON.parse(sessionStorage.getItem("user"))
    this.currentId = user.id
    this.username = user.phone
    this.loadUser()
    this.loadGroup()
    this.connect()
  },

  methods: {
    /**
     * 删除群成员
     * @param removeUserId 成员id
     * @param removeUsername 成员昵称
     */
    remove(removeUserId, removeUsername) {
      removeMember({
        groupId: this.currentGroup.id,
        userId: removeUserId
      }).then(res => {
        if (this.isError(res)) {
          return
        }
        this.$Message.success(`已将${removeUsername}移出`)
        // 移出后, 关闭窗口, 刷新数据
        this.removeModal = false
        this.loadGroupMember(this.currentGroup.id)
      })
    },

    /**
     * 上传文件
     * @param file 文件
     */
    upload(file) {
      this.file = file
      let formData = new FormData();
      formData.append('file', this.file);

      fileUpload(formData).then(res => {
        if (res.body.code != 1) {
          this.$Message.error(res.body.msg)
          return
        }
        this.filePath = res.body.data
      })
    },

    /**
     * 新建群聊
     */
    addGroup() {
      if (!this.filePath || !this.groupName) {
        this.$Message.error("头像或名字为空")
        return
      }

      createGroup({
        avatar: this.filePath,
        name: this.groupName
      }).then(res => {
        if (this.isError(res)) {
          return
        }
        this.loadGroup()
      })
    },

    /**
     * 切换群聊
     * @param data
     */
    changeGroup(data) {
      // 设置回复的群聊
      this.groupReceiver = data.id

      // 选择当前群聊要展示的聊天记录
      this.nowChat = this.groupsChat.get(data.id)

      getGroupInfo(data.id).then(res => {
        if (this.isError(res)) {
          return
        }
        this.groupMember = res.body.data["userList"]
        // 将当前组设置为选中的组
        this.currentGroup = res.body.data
        // 设置选中样式
        this.groups.forEach(it => {
          it.style = "option-list"
        })
        this.groups[data.index].style = "option-list active"
      })
    },

    /**
     * 加载群成员
     * @param groupId 群id
     */
    loadGroupMember(groupId) {
      getGroupInfo(groupId).then(res => {
        if (this.isError(res)) {
          return
        }
        this.groupMember = res.body.data["userList"]
      })
    },

    /**
     * 连接websocket
     */
    connect() {
      if (this.username) {
        this.stompClient = Stomp.over(new SockJS('http://localhost:8081/ws'))
        this.stompClient.connect({}, this.onConnected, err => {
          alert(err)
        })
      }
    },

    /**
     * websocket连接成功
     */
    onConnected() {
      // 订阅该话题
      this.stompClient.subscribe(`/user/${this.username}/groupChat`, this.onGroupMessageReceived)
      this.stompClient.send("/web/chat.join", {}, JSON.stringify({sender: this.username, type: 'JOIN'}))
    },

    /**
     * 接收到群聊消息被调用
     */
    onGroupMessageReceived(payload) {
      const message = JSON.parse(payload.body)
      // 手机号替换为昵称
      getUserByPhone(message.sender).then(res => {
        if (this.isError(res)) {
          return
        }
        // 将消息添加到集合
        // 根据选择的群聊, 放入聊天记录
        let data = res.body.data
        this.groupsChat.get(parseInt(message.receiver)).push({
          messageSender: data.nickname,
          messageContent: message.content,
          avatar: data.avatar,
          userId: data.id,
          key: Date.now(),
        })
      })
    },

    /**
     * 发送群聊消息
     */
    sendGroupMessage(event) {
      event.preventDefault()
      // 当消息和客户端都存在, 这时才能发送消息
      if (this.sendData && this.stompClient) {
        // 封装消息
        const chatMessage = {
          sender: this.username,
          content: this.sendData,
          receiver: this.groupReceiver,
          type: 'GROUP_CHAT',
        }
        // 将消息发送到后端
        this.stompClient.send("/web/chat.sendGroupMessage", {}, JSON.stringify(chatMessage))

        // 消息发送完毕后, 清空发送框
        this.sendData = ''
      }
    },

    /**
     * 加载当前用户加入的所有群聊
     */
    loadGroup() {
      // 创建map, 用当前群id作为key, value存储该群的聊天记录
      this.groupsChat = new Map()
      getUserJoinGroup(this.currentId).then(res => {
        if (res.body.code != 1) {
          this.$Message.error(res.body.msg)
          return
        }

        this.groups = res.body.data
        // 为每个群聊创建一个聊天记录
        this.groups.forEach(it => {
          // 为组设置动态样式
          it.style = "option-list"

          // 根据群id, 查找群聊中所有的聊天记录
          let content = []
          getGroupChat(it.id).then(res => {
            /*
              {groupId: [{messageContent: "", messageSender: ""}], groupId: [{messageContent: "", messageSender: ""}]}
             */
            if (this.isError(res)) {
              return
            }

            // 封装每个组的聊天记录
            res.body.data.forEach(it => {
              content.push({
                key: it.id,
                messageContent: it.content,
                messageSender: it.nickname,
                avatar: it.avatar,
                userId: it.userId,
                createTime: it.createTime
              })
            })
            this.groupsChat.set(it.id, content)
          })
        })
      })
    },

    /**
     * 响应是否错误
     * @param res 响应对象
     * @returns {boolean} true错误
     */
    isError(res) {
      if (res.body.code != 1) {
        this.$Message.error(res.body.msg)
        return true
      }
      return false
    },

    /**
     * 加载当前用户
     */
    loadUser() {
      getUser(this.currentId).then(res => {
        if (this.isError(res)) {
          return
        }
        this.username = res.body.data.phone
        this.currentUser = res.body.data
      })
    },

    /**
     * 选中某个选项, 改变样式
     * @param index 选项下标
     */
    changeOption(index) {
      // 改变选中样式
      this.optionStyle.forEach(it => {
        it.clazz = "option-list"
      })
      this.optionStyle[index].clazz = "option-list active"
    }
  }
}
</script>

<style scoped>
.img-box {
  height: fit-content;
  display: inline-block;
  border: 1px solid black;
}

.member td {
  padding-right: 10px;
}

.chatBox {
  position: relative;
  width: 100%;
  height: calc(100% - 120px);
  padding: 10px;
  overflow-y: auto;
}

.chat-list {
  margin-left: 20px;
}

.group-list {
  height: 550px;
  overflow-y: scroll;
}

.search {
  width: inherit;
  height: 50px;
  background: #3f2e57;
}

.chat-top {
  width: inherit;
  height: 50px;
  background: white;
  border-bottom: 1px solid #999;
}

.chat-top > img {
  margin: 5px 10px 0 10px;
}

.chat-top > span {
  position: absolute;
  top: 95px;
}

.search > input {
  width: 190px;
  background: #999;
  border: 0;
  border-radius: 20px;
  margin: 10px 10px 0 10px;
}

.search > Button {
  margin-top: 10px;
  border-radius: 20px;
  background: #999;
}

.option-list {
  transition: .3s;
}

.active {
  background: #483663;
}

.option-list:hover {
  background: #483663;
}

.box {
  margin: 0 auto;
  height: 600px;
  width: 1200px;
  background: white;
  border-radius: 10px;
  overflow: hidden;
}

.box > .option {
  display: inline-block;
  vertical-align: top;
  width: 60px;
  height: inherit;
  background: #4b2e64;
}

.box > .list {
  vertical-align: top;
  display: inline-block;
  width: 250px;
  height: inherit;
  background: #3f2e57;
}

div::-webkit-scrollbar {
  width: 0;
}

.box > .chat-room {
  vertical-align: top;
  display: inline-block;
  width: 600px;
  height: inherit;
  background: white;
}

.box > .setting {
  vertical-align: top;
  display: inline-block;
  width: 290px;
  height: inherit;
  background: white;
  border-left: 1px solid #999;
}

.option > .avatar {
  width: 40px;
  height: 40px;
  margin: 20px auto;
}

.img {
  width: 40px;
  height: 40px;
  border-radius: 40px;
}
</style>